#include "DepthTexture.hpp"
#include "Core/Buffer/LumineBuffer.hpp"
#include "Core/Context/LumineContext.hpp"

vk::Format Lumine::DepthTexture::findDepthFormat()
{
    std::vector<vk::Format> formats = {vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint};
    return findSupportedFormat(formats, vk::ImageTiling::eOptimal, vk::FormatFeatureFlagBits::eDepthStencilAttachment);
}

vk::Format Lumine::DepthTexture::findSupportedFormat(const std::vector<vk::Format> &candidates, vk::ImageTiling tiling, vk::FormatFeatureFlags features)
{
    for (const auto format : candidates)
    {
        vk::FormatProperties props = LumineContext::Get().PhysicalDevice().getFormatProperties(format);
        if (tiling == vk::ImageTiling::eLinear && (props.linearTilingFeatures & features) == features)
        {
            return format;
        }
        if (tiling == vk::ImageTiling::eOptimal && (props.optimalTilingFeatures & features) == features)
        {
            return format;
        }
    }
    throw std::runtime_error("failed to find supported format!");
}

bool Lumine::DepthTexture::hasStencilComponent(vk::Format format)
{
    return format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint;
}

void Lumine::DepthTexture::createTextureImage()
{
    auto usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
    auto props = vk::MemoryPropertyFlagBits::eDeviceLocal;
    createImage(m_extent.width, m_extent.height, m_format, vk::ImageTiling::eOptimal, usage, props, textureImage, m_memory);
    transitionImageLayout(
        textureImage,
        vk::ImageAspectFlagBits::eDepth,
        vk::ImageLayout::eUndefined,
        vk::ImageLayout::eDepthStencilAttachmentOptimal);
}

void Lumine::DepthTexture::createTextureImageView()
{
    vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eDepth;
    if(hasStencilComponent(m_format)){
        aspect = aspect | vk::ImageAspectFlagBits::eStencil;
    }
    LumineBuffer::createTextureImageView(m_imageView, aspect, textureImage, m_format);
}

void Lumine::DepthTexture::Create()
{
    m_format = findDepthFormat();
    createTextureImage();
    createTextureImageView();
}